package danran.rpc.client.loadbalance;

import danran.rpc.common.service.CacheItem;
import danran.rpc.common.service.Service;
import danran.rpc.common.util.CacheItemUtil;
import danran.rpc.exception.InvalidArgumentException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 * @Classname Balance
 * @Description TODO
 * @Date 2021/9/27 15:58
 * @Created by ASUS
 */
public class Updater {

    private Map<String, CacheItem> cacheList;

    /**
     * 高负载阈值
     */
    private double LOAD_HIGH = 0.8;

    /**
     * 低负载阈值
     */
    private double LOAD_LOW = 0.2;

    /**
     * 放缩因子
     */
    private double FACTOR = 0.5;

    public void setLOAD_HIGH(double LOAD_HIGH) {
        this.LOAD_HIGH = LOAD_HIGH;
    }

    public void setLOAD_LOW(double LOAD_LOW) {
        this.LOAD_LOW = LOAD_LOW;
    }

    public void setFACTOR(double FACTOR) {
        this.FACTOR = FACTOR;
    }

    public void setCacheList(Map<String, CacheItem> cacheList) {
        this.cacheList = cacheList;
    }

    /**
     * @param newCacheList 从注册中心中拉取的新的服务列表（包含负载信息）
     */
    public void updateCacheList(Map<String, CacheItem> newCacheList) {
        if (newCacheList == null) throw new InvalidArgumentException("新拉取服务列表为null...");
        // 新拉取的服务列表
        Set<Map.Entry<String, CacheItem>> entries = newCacheList.entrySet();
        // 对每一个服务进行更新
        for (Map.Entry<String, CacheItem> entry : entries) {
            String serviceName = entry.getKey();
            CacheItem newItem = entry.getValue();
            // 当前的本地缓存中包含新列表中服务项，更新本地缓存即可
            if (cacheList.containsKey(serviceName)) {
                CacheItem oldItem = cacheList.get(serviceName);
                CacheItem cacheItem = updateParameters(newItem, oldItem);
                cacheList.put(serviceName, cacheItem);
            } else { // 不包含直接按照默认值生成新的权重缓存对象，放入本地缓存中
                CacheItem cacheItem = updateParameters(newItem, null);
                cacheList.put(serviceName, cacheItem);
            }
        }

        entries = cacheList.entrySet();
        for (Map.Entry<String, CacheItem> entry : entries) {
            CacheItem value = entry.getValue();
            List<Double> W = value.getW();
            List<double[]> a = value.getA();
            List<double[]> w_var = value.getW_var();
            List<Service> serviceList = value.getServiceList();
            int size = W.size();
            for (int i = 0; i < size; i++) {
                Service service = serviceList.get(i);
                double load = 0.0;
                double[] a_ = a.get(i);
                double[] w_var_ = w_var.get(i);
                double l_1 = a_[0] * service.getCpuLoad();
                double l_2 = a_[1] * service.getIoLoad();
                double l_3 = a_[2] * service.getStorageLoad();
                double l_4 = a_[3] * service.getTapeLoad();
                double t = l_1 + l_2 + l_3 + l_4;
                l_1 = l_1 / t;
                l_2 = l_2 / t;
                l_3 = l_3 / t;
                l_4 = l_4 / t;
                load = l_1 + l_2 + l_3 + l_4;
                if (load < LOAD_LOW) {
                    load = load + FACTOR * (LOAD_LOW - load);
                } else if (LOAD_LOW <= load && load <= LOAD_HIGH) {
                    load = load + FACTOR * ((LOAD_LOW + LOAD_HIGH) / 2 - load);
                } else {
                    load = load + FACTOR * (LOAD_HIGH - load);
                }
                W.set(i, load);
            }

        }
    }

    private CacheItem updateParameters(CacheItem newItem, CacheItem oldItem) {
        // 判断服务项目列表提供者是否发生变化
        if (CacheItemUtil.isSame(newItem, oldItem)) {
            // 服务提供者没有发生变化 ==> （参数状态转移）更新参数权重
            List<Service> curServiceList = newItem.getServiceList();

            List<Double> o_W = oldItem.getW();
            List<double[]> old_w_var = oldItem.getW_var();

            // 首先求每个性能指标的占用率
            double cpu = 0, io = 0, storage = 0, tape = 0;
            for (Service service : curServiceList) {
                cpu += service.getCpuLoad();
                io += service.getIoLoad();
                storage += service.getStorageLoad();
                tape += service.getTapeLoad();
            }
            // 服务器的四个性能指标的平均占用率
            cpu /= curServiceList.size() * 1.0;
            io /= curServiceList.size() * 1.0;
            storage /= curServiceList.size() * 1.0;
            tape /= curServiceList.size() * 1.0;

            // 新建一个缓存项目，用于存储更新的权重以及参数
            CacheItem updatedItem = new CacheItem(curServiceList);
            List<double[]> updatedItemW_var = updatedItem.getW_var();
            List<double[]> update_a = updatedItem.getA();

            for (int i = 0; i < updatedItemW_var.size(); i++) {
                double[] n_w_var = updatedItemW_var.get(i);
                double[] o_w_var = old_w_var.get(i);
                Service cur_service_instance = curServiceList.get(i);
                double[] a = update_a.get(i);

                n_w_var[0] = o_w_var[0] + (1 - cpu / cur_service_instance.getCpuLoad());
                n_w_var[1] = o_w_var[1] + (1 - io / cur_service_instance.getIoLoad());
                n_w_var[2] = o_w_var[2] + (1 - storage / cur_service_instance.getStorageLoad());
                n_w_var[3] = o_w_var[3] + (1 - tape / cur_service_instance.getTapeLoad());

                double total = Arrays.stream(n_w_var).sum();
                for (int j = 0; j < n_w_var.length; j++) {
                    a[j] = n_w_var[j] / total;
                }

                updatedItem.setL(new ArrayList<>(o_W));
            }

            return updatedItem;

        } else { // 服务列表中的至少一个服务发生了变化，根据默认系数配置参数权重
            List<Service> curServiceList = newItem.getServiceList();
            // 首先求每个性能指标的占用率
            double cpu = 0, io = 0, storage = 0, tape = 0;
            for (Service service : curServiceList) {
                cpu += service.getCpuLoad();
                io += service.getIoLoad();
                storage += service.getStorageLoad();
                tape += service.getTapeLoad();
            }
            // 服务器的四个性能指标的平均占用率
            cpu /= curServiceList.size() * 1.0;
            io /= curServiceList.size() * 1.0;
            storage /= curServiceList.size() * 1.0;
            tape /= curServiceList.size() * 1.0;

            // 新建一个缓存项目，用于存储更新的权重以及参数
            CacheItem updatedItem = new CacheItem(curServiceList);
            List<double[]> updatedItemW_var = updatedItem.getW_var();
            List<double[]> update_a = updatedItem.getA();

            for (int i = 0; i < updatedItemW_var.size(); i++) {
                double[] n_w_var = updatedItemW_var.get(i);
                double[] o_w_var = new double[]{0.25, 0.25, 0.25, 0.25};
                Service cur_service_instance = curServiceList.get(i);
                double[] a = update_a.get(i);

                n_w_var[0] = o_w_var[0] + (1 - cpu / cur_service_instance.getCpuLoad());
                n_w_var[1] = o_w_var[1] + (1 - io / cur_service_instance.getIoLoad());
                n_w_var[2] = o_w_var[2] + (1 - storage / cur_service_instance.getStorageLoad());
                n_w_var[3] = o_w_var[3] + (1 - tape / cur_service_instance.getTapeLoad());

                double total = Arrays.stream(n_w_var).sum();
                for (int j = 0; j < n_w_var.length; j++) {
                    a[j] = n_w_var[j] / total;
                }

            }
            return updatedItem;
        }
    }

}
